import bpy
import array
import math
import os

from ogreEx.all import uid
from ogreEx.all import material_name
from ogreEx.axis import swap
from ogreEx.xml.CRDocument import RDocument
from ogreEx.helper import clean_object_name


def bake_terrain( ob, normalize=True ):
    assert ob.collision_mode == 'TERRAIN'
    terrain = None
    for child in ob.children:
        if child.subcollision and child.name.startswith('TERRAIN'):
            terrain = child
            break
    assert terrain
    data = terrain.to_mesh(bpy.context.scene, True, "PREVIEW")
    raw = [ v.co.z for v in data.vertices ]
    Zmin = min( raw )
    Zmax = max( raw )
    depth = Zmax-Zmin
    m = 1.0 / depth

    rows = []
    i = 0
    for x in range( ob.collision_terrain_x_steps ):
        row = []
        for y in range( ob.collision_terrain_y_steps ):
            v = data.vertices[ i ]
            if normalize:
                z = (v.co.z - Zmin) * m
            else:
                z = v.co.z
            row.append( z )
            i += 1
        if x%2:
            row.reverse()   # blender grid prim zig-zags
        rows.append( row )

    return {'data':rows, 'min':Zmin, 'max':Zmax, 'depth':depth}

def save_terrain_as_NTF( path, ob ):    # Tundra format - hardcoded 16x16 patch format
    info = bake_terrain( ob )
    url = os.path.join( path, '%s.ntf' % clean_object_name(ob.data.name) )    
    f = open(url, "wb")
    # Header
    buf = array.array("I")
    xs = ob.collision_terrain_x_steps
    ys = ob.collision_terrain_y_steps
    xpatches = int(xs/16)
    ypatches = int(ys/16)
    header = [ xpatches, ypatches ]
    buf.fromlist( header )
    buf.tofile(f)
    # Body
    rows = info['data']
    for x in range( xpatches ):
        for y in range( ypatches ):
            patch = []
            for i in range(16):
                for j in range(16):
                    v = rows[ (x*16)+i ][ (y*16)+j ]
                    patch.append( v )
            buf = array.array("f")
            buf.fromlist( patch )
            buf.tofile(f)
    f.close()
    path,name = os.path.split(url)
    R = {
        'url':url, 'min':info['min'], 'max':info['max'], 'path':path, 'name':name,
        'xpatches': xpatches, 'ypatches': ypatches,
        'depth':info['depth'],
        }
    return R

def get_lights_by_type( T ):
    r = []
    for ob in bpy.context.scene.objects:
        if ob.type=='LAMP':
            if ob.data.type==T: r.append( ob )
    return r

class _TXML_(object):

    '''
  <component type="EC_Script" sync="1" name="myscript">
   <attribute value="" name="Script ref"/>
   <attribute value="false" name="Run on load"/>
   <attribute value="0" name="Run mode"/>
   <attribute value="" name="Script application name"/>
   <attribute value="" name="Script class name"/>
  </component>
    '''


    def create_tundra_document( self, context ):
        # todo: Make a way in the gui to give prefix for the refs
        # This can be very useful if you want to give deployment URL
        # eg. "http://www.myassets.com/myscene/". By default this needs
        # to be an empty string, it will operate best for local preview
        # and importing the scene content to existing scenes with relative refs.
        proto = ''

        doc = RDocument()
        scn = doc.createElement('scene')
        doc.appendChild( scn )

        # EC_Script
        if 0: # todo: tundra bug (what does this mean?)
            e = doc.createElement( 'entity' )
            doc.documentElement.appendChild( e )
            e.setAttribute('id', len(doc.documentElement.childNodes)+1 )

            c = doc.createElement( 'component' ); e.appendChild( c )
            c.setAttribute( 'type', 'EC_Script' )
            c.setAttribute( 'sync', '1' )
            c.setAttribute( 'name', 'myscript' )

            a = doc.createElement('attribute'); c.appendChild( a )
            a.setAttribute('name', 'Script ref')
            #a.setAttribute('value', "%s%s"%(proto,TUNDRA_GEN_SCRIPT_PATH) )

            a = doc.createElement('attribute'); c.appendChild( a )
            a.setAttribute('name', 'Run on load')
            a.setAttribute('value', 'true' )

            a = doc.createElement('attribute'); c.appendChild( a )
            a.setAttribute('name', 'Run mode')
            a.setAttribute('value', '0' )

            a = doc.createElement('attribute'); c.appendChild( a )
            a.setAttribute('name', 'Script application name')
            a.setAttribute('value', 'blender2ogre' )


        # Check lighting settings
        sun = hemi = None
        if get_lights_by_type('SUN'  ): sun  = get_lights_by_type('SUN'  )[0]
        if get_lights_by_type('HEMI' ): hemi = get_lights_by_type('HEMI' )[0]
        
        # Environment        
        if bpy.context.scene.world.mist_settings.use_mist or sun or hemi:
            e = doc.createElement( 'entity' )
            doc.documentElement.appendChild( e )
            e.setAttribute('id', len(doc.documentElement.childNodes)+1 )

            # EC_Fog
            c = doc.createElement( 'component' ); e.appendChild( c )
            c.setAttribute( 'type', 'EC_Fog' )
            c.setAttribute( 'sync', '1'      )
            c.setAttribute( 'name', 'Fog'    )

            a = doc.createElement('attribute'); c.appendChild( a )
            a.setAttribute('name', 'Color')
            if bpy.context.scene.world.mist_settings.use_mist:
                A = bpy.context.scene.world.mist_settings.intensity
                R,G,B = bpy.context.scene.world.horizon_color
                a.setAttribute('value', '%s %s %s %s'%(R,G,B,A))
            else:
                a.setAttribute('value', '0.4 0.4 0.4 1.0')

            if bpy.context.scene.world.mist_settings.use_mist:
                mist = bpy.context.scene.world.mist_settings

                a = doc.createElement('attribute'); c.appendChild( a )
                a.setAttribute('name', 'Start distance')
                a.setAttribute('value', mist.start)

                a = doc.createElement('attribute'); c.appendChild( a )
                a.setAttribute('name', 'End distance')
                a.setAttribute('value', mist.start+mist.depth)

            a = doc.createElement('attribute'); c.appendChild( a )
            a.setAttribute('name', 'Exponential density')
            a.setAttribute('value', 0.001)

            # EC_EnvironmentLight
            c = doc.createElement( 'component' ); e.appendChild( c )
            c.setAttribute( 'type', 'EC_EnvironmentLight' )
            c.setAttribute( 'sync', '1' )
            c.setAttribute( 'name', 'Environment Light' )


            a = doc.createElement('attribute'); c.appendChild( a )
            a.setAttribute('name', 'Sunlight color')
            if sun:
                R,G,B = sun.data.color
                a.setAttribute('value', '%s %s %s 1' %(R,G,B))
            else:
                a.setAttribute('value', '0 0 0 1')

            a = doc.createElement('attribute'); c.appendChild( a )
            a.setAttribute('name', 'Brightness')    # brightness of sunlight
            if sun:
                a.setAttribute('value', sun.data.energy*10)     # 10=magic
            else:
                a.setAttribute('value', '0')

            a = doc.createElement('attribute'); c.appendChild( a )
            a.setAttribute('name', 'Ambient light color')
            if hemi:
                R,G,B = hemi.data.color * hemi.data.energy * 3.0
                if R>1.0: R=1.0
                if G>1.0: G=1.0
                if B>1.0: B=1.0
                a.setAttribute('value', '%s %s %s 1' %(R,G,B))
            else:
                a.setAttribute('value', '0 0 0 1')

            a = doc.createElement('attribute'); c.appendChild( a )
            a.setAttribute('name', 'Sunlight direction vector')
            a.setAttribute('value', '-0.25 -1.0 -0.25')   # TODO, get the sun rotation from blender

            a = doc.createElement('attribute'); c.appendChild( a )
            a.setAttribute('name', 'Sunlight cast shadows')
            a.setAttribute('value', 'true')

        # EC_SkyX
        if context.scene.world.ogre_skyX:
            c = doc.createElement( 'component' ); e.appendChild( c )
            c.setAttribute( 'type', 'EC_SkyX' )
            c.setAttribute( 'sync', '1'       )
            c.setAttribute( 'name', 'SkyX'    )

            a = doc.createElement('attribute'); a.setAttribute('name', 'Weather (volumetric clouds only)')
            den = (
                context.scene.world.ogre_skyX_cloud_density_x,
                context.scene.world.ogre_skyX_cloud_density_y
            )
            a.setAttribute('value', '%s %s' %den)
            c.appendChild( a )

            config = (
                ('time', 'Time multiplier'),
                ('volumetric_clouds','Volumetric clouds'),
                ('wind','Wind direction'),
            )
            for bname, aname in config:
                a = doc.createElement('attribute')
                a.setAttribute('name', aname)
                s = str( getattr(context.scene.world, 'ogre_skyX_'+bname) )
                a.setAttribute('value', s.lower())
                c.appendChild( a )

        return doc

    # Creates new Tundra entity
    def tundra_entity( self, doc, ob, path='/tmp', collision_proxies=[], parent=None, matrix=None,visible=True ):
        assert not ob.subcollision

        #  Tundra TRANSFORM
        if not matrix:
            matrix = ob.matrix_world.copy()

        # todo: Make a way in the gui to give prefix for the refs
        # This can be very useful if you want to give deployment URL
        # eg. "http://www.myassets.com/myscene/". By default this needs
        # to be an empty string, it will operate best for local preview
        # and importing the scene content to existing scenes with relative refs.
        proto = ''
        
        # Entity
        entityid = uid(ob)
        objectname = clean_object_name(ob.name)
        print("  Creating Tundra Enitity with ID", entityid)
        
        e = doc.createElement( 'entity' )
        doc.documentElement.appendChild( e )
        e.setAttribute('id', entityid)

        # EC_Name
        print ("    - EC_Name with", objectname)
        
        c = doc.createElement('component'); e.appendChild( c )
        c.setAttribute('type', "EC_Name")
        c.setAttribute('sync', '1')
        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', "name" )
        a.setAttribute('value', objectname )
        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', "description" )
        a.setAttribute('value', "" )


        # EC_Placeable
        print ("    - EC_Placeable ")

        c = doc.createElement('component'); e.appendChild( c )
        c.setAttribute('type', "EC_Placeable")
        c.setAttribute('sync', '1')
        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', "Transform" )
        x,y,z = swap(matrix.to_translation())
        loc = '%6f,%6f,%6f' %(x,y,z)
        x,y,z = swap(matrix.to_euler())
        x = math.degrees( x ); y = math.degrees( y ); z = math.degrees( z )
        if   ob.type == 'CAMERA': x -= 90
        elif ob.type == 'LAMP':   x += 90
        rot = '%6f,%6f,%6f' %(x,y,z)
        x,y,z = swap(matrix.to_scale())
        scl = '%6f,%6f,%6f' %(abs(x),abs(y),abs(z))		# Tundra2 clamps any negative to zero
        a.setAttribute('value', "%s,%s,%s" %(loc,rot,scl) )

        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', "Show bounding box" )
        a.setAttribute('value', "false" )
        # Don't mark bounding boxes to show in Tundra!
        #if ob.show_bounds or ob.type != 'MESH':
        #    a.setAttribute('value', "true" )
        #else:
        #    a.setAttribute('value', "false" )
        
        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', "Visible" )
        if visible:
            a.setAttribute('value', 'true') # overrides children's setting - not good!
        else:
            a.setAttribute('value', 'false')
            
        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', "Selection layer" )
        a.setAttribute('value', 1)

        # Tundra parenting to EC_Placeable. 
        # todo: Verify this inserts correct ent name or id here.
        #   <attribute value="" name="Parent entity ref"/>
        #   <attribute value="" name="Parent bone name"/>
        if parent:
            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', "Parent entity ref" )
            a.setAttribute('value', parent)


        if ob.type != 'MESH':
            c = doc.createElement('component'); e.appendChild( c )
            c.setAttribute('type', 'EC_Name')
            c.setAttribute('sync', '1')

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', "name" )
            a.setAttribute('value', objectname)
            
        # EC_Sound: Supports wav and ogg
        if ob.type == 'SPEAKER':
            print ("    - EC_Sound")
            c = doc.createElement('component'); e.appendChild( c )
            c.setAttribute('type', 'EC_Sound')
            c.setAttribute('sync', '1')

            if ob.data.sound:
                abspath = bpy.path.abspath( ob.data.sound.filepath )
                soundpath, soundfile = os.path.split( abspath )
                soundref = "%s%s" % (proto, soundfile)
                print ("      Sounds ref:", soundref)                
                a = doc.createElement('attribute'); c.appendChild(a)
                a.setAttribute('name', 'Sound ref' )
                a.setAttribute('value', soundref)
                if not os.path.isfile( os.path.join(path,soundfile) ):
                    open( os.path.join(path,soundfile), 'wb' ).write( open(abspath,'rb').read() )

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', 'Sound radius inner' )
            a.setAttribute('value', ob.data.cone_angle_inner)

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', 'Sound radius outer' )
            a.setAttribute('value', ob.data.cone_angle_outer)

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', 'Sound gain' )
            a.setAttribute('value', ob.data.volume)

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', 'Play on load' )
            if ob.data.play_on_load:
                a.setAttribute('value', 'true')
            else:
                a.setAttribute('value', 'false')

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', 'Loop sound' )
            if ob.data.loop:
                a.setAttribute('value', 'true')
            else:
                a.setAttribute('value', 'false')

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', 'Spatial' )
            if ob.data.use_spatial:
                a.setAttribute('value', 'true')
            else:
                a.setAttribute('value', 'false')

        # EC_Camera
        ''' todo: This is really not very helpful. Apps define
            camera logic in Tundra. By default you will have
            a freecamera to move around the scene etc. This created
            camera wont be activated except if a script does so.
            Best leave camera (creation) logic for the inworld apps.
            At least remove the default "export cameras" for txml. '''
        if ob.type == 'CAMERA':
            print ("    - EC_Camera")
            
            c = doc.createElement('component'); e.appendChild( c )
            c.setAttribute('type', 'EC_Camera')
            c.setAttribute('sync', '1')

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', "Up vector" )
            a.setAttribute('value', '0.0 1.0 0.0')

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', "Near plane" )
            a.setAttribute('value', '0.01')

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', "Far plane" )
            a.setAttribute('value', '2000')

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', "Vertical FOV" )
            a.setAttribute('value', '45')

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', "Aspect ratio" )
            a.setAttribute('value', '')


        NTF = None

        # EC_Rigidbody
        # Any object can have physics, although it needs
        # EC_Placeable to have position etc.
        if ob.physics_mode != 'NONE' or ob.collision_mode != 'NONE':
            TundraTypes = {
                'BOX'           : 0,
                'SPHERE'        : 1,
                'CYLINDER'      : 2,
                'CONE'          : 0, # Missing in Tundra
                'CAPSULE'       : 3,
                'TRIANGLE_MESH' : 4,
                #'HEIGHT_FIELD' : 5, # Missing in Blender
                'CONVEX_HULL'   : 6
            }


            com = doc.createElement('component'); e.appendChild( com )
            com.setAttribute('type', 'EC_RigidBody')
            com.setAttribute('sync', '1')

            # Mass
            # * Does not affect static collision types (TriMesh and ConvexHull)
            # * You can have working collisions with mass 0
            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Mass')
            if ob.physics_mode == 'RIGID_BODY':
                a.setAttribute('value', ob.game.mass)
            else:
                a.setAttribute('value', '0.0')


            SHAPE = a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Shape type')
            a.setAttribute('value', TundraTypes[ ob.game.collision_bounds_type ] )

            print ("    - EC_RigidBody with shape type", TundraTypes[ob.game.collision_bounds_type])
            M = ob.game.collision_margin
            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Size')
            if ob.game.collision_bounds_type in 'TRIANGLE_MESH CONVEX_HULL'.split():
                a.setAttribute('value', '%s %s %s' %(1.0+M, 1.0+M, 1.0+M) )
            else:
                #x,y,z = swap(ob.matrix_world.to_scale())
                x,y,z = swap(ob.dimensions)
                a.setAttribute('value', '%s %s %s' %(abs(x)+M,abs(y)+M,abs(z)+M) )

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Collision mesh ref')
            #if ob.game.use_collision_compound:
            if ob.collision_mode == 'DECIMATED':
                proxy = None
                for child in ob.children:
                    if child.subcollision and child.name.startswith('DECIMATED'):
                        proxy = child; break
                if proxy:
                    collisionref = "%s_collision_%s.mesh" % (proto, proxy.data.name)
                    a.setAttribute('value', collisionref)
                    if proxy not in collision_proxies:
                        collision_proxies.append( proxy )
                else:
                    print('[WARNINIG]: Collision proxy mesh not found' )
                    assert 0

            elif ob.collision_mode == 'TERRAIN':
                NTF = save_terrain_as_NTF( path, ob )
                SHAPE.setAttribute( 'value', '5' )  # HEIGHT_FIELD

            elif ob.type == 'MESH':
                # todo: Remove this. There is no need to set mesh collision ref
                # if TriMesh or ConvexHull is used, it will be auto picked from EC_Mesh
                # in the same Entity.
                collisionref = "%s%s.mesh" % (proto, clean_object_name(ob.data.name))
                a.setAttribute('value', collisionref)

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Friction')
            #avg = sum( ob.game.friction_coefficients ) / 3.0
            a.setAttribute('value', ob.physics_friction)

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Restitution')
            a.setAttribute('value', ob.physics_bounce)

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Linear damping')
            a.setAttribute('value', ob.game.damping)

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Angular damping')
            a.setAttribute('value', ob.game.rotation_damping)


            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Linear factor')
            a.setAttribute('value', '1.0 1.0 1.0')

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Angular factor')
            a.setAttribute('value', '1.0 1.0 1.0')


            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Kinematic')
            a.setAttribute('value', 'false' )

            # todo: Find out what Phantom actually means and if this
            # needs to be set for NONE collision rigids. I don't actually
            # see any reason to make EC_RigidBody if collision is NONE
            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Phantom')
            if ob.collision_mode == 'NONE':
                a.setAttribute('value', 'true' )
            else:
                a.setAttribute('value', 'false' )

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Draw Debug')
            a.setAttribute('value', 'false' )

            # Never mark rigids to have draw debug, it can 
            # be toggled in tundra for visual debugging.
            #if ob.collision_mode == 'NONE':
            #    a.setAttribute('value', 'false' )
            #else:
            #    a.setAttribute('value', 'true' )
            
            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Linear velocity')
            a.setAttribute('value', '0.0 0.0 0.0')

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Angular velocity')
            a.setAttribute('value', '0.0 0.0 0.0')

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Collision Layer')
            a.setAttribute('value', -1)

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Collision Mask')
            a.setAttribute('value', -1)

        # EC_Terrain
        if NTF:
            xp = NTF['xpatches']
            yp = NTF['ypatches']
            depth = NTF['depth']
            com = doc.createElement('component'); e.appendChild( com )
            com.setAttribute('type', 'EC_Terrain')
            com.setAttribute('sync', '1')

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Transform')
            x,y,z = ob.dimensions
            sx,sy,sz = ob.scale
            x *= 1.0/sx
            y *= 1.0/sy
            z *= 1.0/sz
            #trans = '%s,%s,%s,' %(-xp/4, -z/2, -yp/4)
            trans = '%s,%s,%s,' %(-xp/4, -depth, -yp/4)
            # scaling in Tundra happens after translation
            nx = x/(xp*16)
            ny = y/(yp*16)
            trans += '0,0,0,%s,%s,%s' %(nx,depth, ny)
            a.setAttribute('value', trans )

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Grid Width')
            a.setAttribute('value', xp)

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Grid Height')
            a.setAttribute('value', yp)

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Tex. U scale')
            a.setAttribute('value', 1.0)

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Tex. V scale')
            a.setAttribute('value', 1.0)

            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Material')
            a.setAttribute('value', '')

            for i in range(4):
                a = doc.createElement('attribute'); com.appendChild( a )
                a.setAttribute('name', 'Texture %s' %i)
                a.setAttribute('value', '')

            # todo: Check that NTF['name'] is the actual valid asset ref
            # and not the disk path.
            heightmapref = "%s%s" % (proto, NTF['name'])
            print ("      Heightmap ref:", heightmapref)                
            a = doc.createElement('attribute'); com.appendChild( a )
            a.setAttribute('name', 'Heightmap')
            a.setAttribute('value', heightmapref )

        # Enitity XML generation done, return the element.
        return e

    # EC_Mesh
    def tundra_mesh( self, e, ob, url, exported_meshes ):
        # todo: Make a way in the gui to give prefix for the refs
        # This can be very useful if you want to give deployment URL
        # eg. "http://www.myassets.com/myscene/". By default this needs
        # to be an empty string, it will operate best for local preview
        # and importing the scene content to existing scenes with relative refs.
        proto = ''
        
        objectname = clean_object_name(ob.data.name)
        meshname = "%s.mesh" % objectname
        meshref = "%s%s.mesh" % (proto, objectname)
        
        print ("    - EC_Mesh")
        print ("      - Mesh ref:", meshref)
        
        if self.EX_MESH:
            murl = os.path.join( os.path.split(url)[0], '%s.mesh'%ob.data.name )
            exists = os.path.isfile( murl )
            if not exists or (exists and self.EX_MESH_OVERWRITE):
                if meshname not in exported_meshes:
                    exported_meshes.append( meshname )
                    self.dot_mesh( ob, os.path.split(url)[0] )

        doc = e.document
        proto = 'local://'      # antont says file:// is also valid

        if ob.find_armature():
            print ("    - EC_AnimationController")
            c = doc.createElement('component'); e.appendChild( c )
            c.setAttribute('type', "EC_AnimationController")
            c.setAttribute('sync', '1')

        c = doc.createElement('component'); e.appendChild( c )
        c.setAttribute('type', "EC_Mesh")
        c.setAttribute('sync', '1')

        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', "Mesh ref" )
        a.setAttribute('value',  meshref)

        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', "Mesh materials" )
        # Query object its materials and make a proper material ref string of it.
        # note: We assume blindly here that the 'submesh' indexes are correct in the material list.
        mymaterials = ob.data.materials
        if mymaterials is not None and len(mymaterials) > 0:
            mymatstring = ''                 # generate ; separated material list
            for mymat in mymaterials:
                if mymat is None:
                    continue
                mymatstring += proto + material_name(mymat) + '.material;'
            mymatstring = mymatstring[:-1]  # strip ending ;
            a.setAttribute('value', mymatstring )
        else:
            # default to nothing to avoid error prints in .txml import
            a.setAttribute('value', "" )

        if ob.find_armature():
            skeletonref = "%s%s.skeleton" % (proto, clean_object_name(ob.data.name))
            print ("      Skeleton ref:", skeletonref)        
            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', "Skeleton ref" )
            a.setAttribute('value', skeletonref)

        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', "Draw distance" )
        if ob.use_draw_distance:
            a.setAttribute('value', ob.draw_distance )
        else:
            a.setAttribute('value', "0" )
        
        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', 'Cast shadows' )
        if ob.cast_shadows:
            a.setAttribute('value', 'true' )
        else:
            a.setAttribute('value', 'false' )

    # EC_Light
    def tundra_light( self, e, ob ):
        '''
            <component type="EC_Light" sync="1">
            <attribute value="1" name="light type"/>
            <attribute value="1 1 1 1" name="diffuse color"/>
            <attribute value="1 1 1 1" name="specular color"/>
            <attribute value="true" name="cast shadows"/>
            <attribute value="29.9999828" name="light range"/>
            <attribute value="1" name="brightness"/>
            <attribute value="0" name="constant atten"/>
            <attribute value="1" name="linear atten"/>
            <attribute value="0" name="quadratic atten"/>
            <attribute value="30" name="light inner angle"/>
            <attribute value="40" name="light outer angle"/>
            </component>
        '''

        if ob.data.type not in 'POINT SPOT'.split():
            return

        doc = e.document

        c = doc.createElement('component'); e.appendChild( c )
        c.setAttribute('type', "EC_Light")
        c.setAttribute('sync', '1')

        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', 'light type' )
        if ob.data.type=='POINT': a.setAttribute('value', '0' )
        elif ob.data.type=='SPOT': a.setAttribute('value', '1' )
        #2 = directional light.  blender has no directional light?

        R,G,B = ob.data.color
        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', 'diffuse color' )
        if ob.data.use_diffuse:
            a.setAttribute('value', '%s %s %s 1' %(R,G,B) )
        else:
            a.setAttribute('value', '0 0 0 1' )

        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', 'specular color' )
        if ob.data.use_specular:
            a.setAttribute('value', '%s %s %s 1' %(R,G,B) )
        else:
            a.setAttribute('value', '0 0 0 1' )

        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', 'cast shadows' )
        if ob.data.type=='HEMI':
            a.setAttribute('value', 'false' ) # HEMI no .shadow_method
        elif ob.data.shadow_method != 'NOSHADOW':
            a.setAttribute('value', 'true' )
        else:
            a.setAttribute('value', 'false' )

        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', 'light range' )
        a.setAttribute('value', ob.data.distance*2 )

        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', 'brightness' )
        a.setAttribute('value', ob.data.energy )

        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', 'constant atten' )
        a.setAttribute('value', '0' )

        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', 'linear atten' )
        energy = ob.data.energy
        if energy <= 0.0:
            energy = 0.001
        a.setAttribute('value', (1.0/energy)*0.25 )

        a = doc.createElement('attribute'); c.appendChild(a)
        a.setAttribute('name', 'quadratic atten' )
        a.setAttribute('value', '0.0' )

        if ob.data.type=='SPOT':
            outer = math.degrees(ob.data.spot_size) / 2.0
            inner = outer * (1.0-ob.data.spot_blend)

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', 'light inner angle' )
            a.setAttribute('value', '%s'%inner )

            a = doc.createElement('attribute'); c.appendChild(a)
            a.setAttribute('name', 'light outer angle' )
            a.setAttribute('value', '%s' %outer )
